/**
 * Copyright (c) 2012 Nokia Corporation.
 * All rights reserved.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#include "facebookconnect.h"

#include <QDebug>
#include <QCoreApplication>

// Constants
const char *GraphURL = "https://graph.facebook.com/";
const char *AccessTokenString = "access_token";
const char *TokenAddTimeString = "token_addtime";
const char *TokenAddDateString = "token_adddate";
const char *TokenExpiresString = "expires_in";
const char *QueryString = "?";
const char *ParameterSeparator = "&";
const char *ParameterFormat = "%1=%2";


/*!
 Helper function for constructing a query part for URL.
*/
inline QString constructQuery(const QVariantMap &parameters)
{
    QStringList urlItems;

    QMapIterator<QString, QVariant> i(parameters);
    while (i.hasNext()) {
        i.next();
        QString newItem(QString(ParameterFormat).arg(i.key(), i.value().toString()));
        urlItems.append(newItem);
    }
    return urlItems.join(ParameterSeparator);
}


/*!
  \class FacebookConnect
  \brief The FacebookConnect class represents a Facebook connection.
*/


/*!
  Constructor.
*/
FacebookConnect::FacebookConnect(QDeclarativeItem *parent)
    : QDeclarativeItem(parent)
{
    // Create network handling connections.
    connect(&m_networkAccess, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(finished(QNetworkReply*)));
}


/*!
  Constructor.
*/
FacebookConnect::FacebookConnect(const QString &appId,
                                 const QString &organisation,
                                 const QString &appName,
                                 QDeclarativeItem *parent)
    : QDeclarativeItem(parent),
      m_appId(appId),
      m_organisationName(organisation),
      m_applicationName(appName)
{
    // Create network handling connections.
    connect(&m_networkAccess, SIGNAL(finished(QNetworkReply *)),
            this, SLOT(finished(QNetworkReply *)));

    // Check if we have the access token stored in the settings.
    retrieveAccessTokenFromSettings();
}


/*!
  Destructor.
*/
FacebookConnect::~FacebookConnect()
{
}

/*!
  Returns application id of the session.
*/
QString FacebookConnect::appId() const
{
    return m_appId;
}


/*!
  Sets the application id for session.
*/
void FacebookConnect::setAppId(const QString &appId)
{
    if (m_appId != appId) {
        m_appId = appId;
        emit appIdChanged(m_appId);
    }
}


/*!
  Returns access token.
*/
QString FacebookConnect::accessToken() const
{
    return m_accessToken;
}


/*!
  Sets access token, and saves it to settings.
*/
void FacebookConnect::setAccessToken(const QString &accessToken)
{
    QSettings settings(QSettings::UserScope, m_organisationName, m_applicationName);
    settings.setValue(AccessTokenString, accessToken);
    m_accessToken = accessToken;
    emit accessTokenChanged(accessToken);
}


/*!
  Sets access token expiration time and saves it to the settings.
*/
void FacebookConnect::setExpirationTime(const QString &expirationTime)
{
    if (!expirationTime.isEmpty()) {
        QSettings settings(QSettings::UserScope,
                           m_organisationName,
                           m_applicationName);
        settings.setValue(TokenAddTimeString, QTime::currentTime());
        settings.setValue(TokenAddDateString, QDate::currentDate());
        settings.setValue(TokenExpiresString, expirationTime.toInt());
    }
}


/*!
  Make a request with graph path. Parameter requestId is used for identifying
  request and response.
*/
void FacebookConnect::request(const QString &requestId,
                              const QString &graphPath)
{
    request(requestId, graphPath, QVariantMap());
}


/*!
  Makes a request with graph path and parameters. Parameters contain search
  specific keys and values.
*/
void FacebookConnect::request(const QString &requestId,
                              const QString &graphPath,
                              const QVariantMap &parameters)
{
    request(requestId, graphPath, HTTPGet, parameters);
}


/*!
  Makes a request with graph path, parameters and HTTP method defined.
*/
void FacebookConnect::request(const QString &requestId,
                              const QString &graphPath,
                              const HTTPMethod method,
                              const QVariantMap &parameters)
{
    m_requestId = requestId;

    QVariantMap tempParams(parameters);
    QStringList query;
    query << GraphURL << graphPath;

    if (!m_accessToken.isEmpty()) {
        tempParams.insert(AccessTokenString, m_accessToken);
    }

    if (!tempParams.isEmpty()) {
        query << QueryString;
    }

    query << constructQuery(tempParams);

    QNetworkRequest request(QUrl(query.join("")));

    // Only get method is supported for now.
    switch (method) {
    case HTTPPost:
        break;
    case HTTPGet:
        m_networkAccess.get(request);
        break;
    case HTTPPut:
        break;
    case HTTPDelete:
        break;
    default:
        emit requestFailed(m_requestId, "Invalid request method.");
        break;
    }
}


/*!
  This slot handles network reply received from QNetworkAccessManager.
*/
void FacebookConnect::finished(QNetworkReply *reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        emit requestFailed(m_requestId, QString(reply->errorString()));
        return;
    }
    else {
        QByteArray result(reply->readAll());
        emit requestCompleted(m_requestId, result);
        qDebug() << "Result: " << result;
    }

    reply->deleteLater();
}


/*!
  Checks if access token is valid and has not been expired. Also clears
  settings if token is expired. Returns false if access token isn't valid.
*/
bool FacebookConnect::isAuthorized() const
{
    QSettings settings(QSettings::UserScope,
                       m_organisationName,
                       m_applicationName);

    if (settings.value(AccessTokenString).isNull()) {
        return false;
    }

    QTime expirationTime(settings.value(TokenAddTimeString).toTime());
    QDate expirationDate(settings.value(TokenAddDateString).toDate());
    const int deltaTime(settings.value(TokenExpiresString).toInt());
    QDate dateNow(QDate::currentDate());
    QTime timeNow(QTime::currentTime());

    if ((expirationTime.addSecs(deltaTime) > timeNow
            && expirationDate < dateNow)
            || expirationTime.addSecs(deltaTime) < timeNow
            && expirationDate <= dateNow)
    {
        settings.remove(AccessTokenString);
        settings.remove(TokenAddTimeString);
        settings.remove(TokenAddDateString);
        settings.remove(TokenExpiresString);
        return false;
    }

    return true;
}


/*!
  Returns current request id.
*/
QString FacebookConnect::requestId() const
{
    return m_requestId;
}


/*!
  Returns application permissions.
*/
QStringList FacebookConnect::permissions() const
{
    return m_permissions;
}


/*!
  Sets permissions for application.
*/
void FacebookConnect::setPermissions(const QStringList &permissions)
{
    m_permissions = permissions;
    emit permissionsChanged(m_permissions);
}


/*!
  Returns the name of the set organization.
*/
QString FacebookConnect::organization() const
{
    return m_organisationName;
}


/*!
  Sets the name of the organization to \a organization.
*/
void FacebookConnect::setOrganization(const QString &organization)
{
    if (m_organisationName != organization) {
        m_organisationName = organization;

        if (m_accessToken.isEmpty()) {
            retrieveAccessTokenFromSettings();
        }
    }
}


/*!
  Returns the name of the application.
*/
QString FacebookConnect::appName() const
{
    return m_applicationName;
}


/*!
  Sets the name of the organization to \a appName.
*/
void FacebookConnect::setAppName(const QString &appName)
{
    if (m_applicationName != appName) {
        m_applicationName = appName;

        if (m_accessToken.isEmpty()) {
            retrieveAccessTokenFromSettings();
        }
    }
}


/*!
*/
void FacebookConnect::logout()
{
    QSettings settings(QSettings::UserScope,
                       m_organisationName,
                       m_applicationName);
    settings.remove(AccessTokenString);
    settings.remove(TokenAddTimeString);
    settings.remove(TokenAddDateString);
    settings.remove(TokenExpiresString);
    m_accessToken.clear();
    emit logoutCompleted();
}


/*!
  Tries to retrieve the stored access token from the settings.
*/
void FacebookConnect::retrieveAccessTokenFromSettings()
{
    if (!m_organisationName.isEmpty() && !m_applicationName.isEmpty()) {
        QSettings settings(QSettings::UserScope,
                           m_organisationName,
                           m_applicationName);
        QString accessToken = settings.value(AccessTokenString).toString();

        if (!accessToken.isEmpty()) {
            qDebug() << "Retrieved access token from settings:" << accessToken;
            m_accessToken = accessToken;
        }
    }
}
